/*
 * TODO: 暂时不支持多个表单同时共存
 * @Description: 全局校验规则与提示
 * @Author: James324
 * @Date: 2023-09-26 10:21:09
 * @LastEditors: James324
 * @LastEditTime: 2024-02-26 19:01:42
 */
import { getDom, getDomList, getParentToSelector } from '@/utils/dom';
import { Valid, StrategyKey, validErrorMessage } from '@/utils/reg';
import { haveErrorClass, addErrorClass, removeErrorClass, generateClassToId } from '@/utils/valid';
import { isArray } from 'lodash-es';

type Position = 'left' | 'center';
interface Params {
    position: Position; // popover 错误提示的位置，默认居左
    formConfig: ItemConfig[]; // 所需验证表单的配置
    formFields: Ref<any>; // 验证的表单各个字段
    hideErrorText: boolean; // 隐藏错误提示文案
    mountErrId: string; // 挂载错误文本元素父节点
}
export default ({
    position = 'left',
    formConfig,
    formFields,
    hideErrorText,
    mountErrId
}: Partial<Params> = {}) => {
    /* 校验失败提示初始化文案与变量 */
    const popoverErrMsg = ref('输入验证失败！');
    const popoverVisible = ref(false);

    /**
     * 输入框获取焦点
     */
    type Other = {
        config: ItemConfig;
        el?: HTMLElement;
    };

    const inputFocus = (e: any, row: any, other: Other) => {
        if (row.formatError) return; // 存在输入格式问题

        const { validProps, validRules, modelKey } = other.config;

        // 去除所有输入项带有 is-error 错误提示样式
        let isErrorActive = false;
        if (e.type === 'focus') {
            const formItemWrap = getParentToSelector(other.el!, '.q-form-item__content')!;
            isErrorActive = haveErrorClass(formItemWrap);
            // 当前元素的校验 Error 错误信息为激活状态在执行下面逻辑
            if (isErrorActive) {
                removeErrorClass(formItemWrap);
                if (!hideErrorText && !mountErrId) {
                    clearValidErrorDom(formItemWrap);
                }
            }
        }

        if (validProps && modelKey) {
            if (!isErrorActive) return; // TODO:初次输入不作相对应提示
            const validInput = new Valid();
            // 添加必填项校验
            if (validProps.required) {
                validInput.add({ value: row[modelKey] }, StrategyKey.IS_EMPTY, '请完善必填信息');
            }

            if (isArray(validRules)) {
                for (const item of validRules) {
                    if (validInput.isHaveValidItem(item.validType!)) {
                        continue;
                    }

                    validInput.add({ value: row[modelKey] }, item.validType!, item.errMsg!);
                }
            }

            const errValid = validInput.start();

            if (errValid) {
                showErrorPopover(e, errValid.errMsg);
            } else {
                hideErrorPopover();
            }
        }
    };

    /**
     * 展示 Error popover 提示
     */
    const showErrorPopover = (e: any, errMsg: string) => {
        popoverErrMsg.value = errMsg;
        setValidErrorVisible(true);

        nextTick(() => {
            setOffsetPopover(e);
        });
    };

    /**
     * 隐藏 Error popover 提示
     */
    const hideErrorPopover = () => {
        setValidErrorVisible(false);
    };

    /**
     * 表单输入
     */
    type Args = [e: any, row: any, other: any];
    const handleInputDebounce = useDebounceFn((...args: Args) => {
        // 输入节流，时间为 300 ms
        inputFocus.apply(this, args);
    }, 300);
    const handleInput = (...args: Args) => {
        handleInputDebounce(...args);
    };

    /**
     * 输入获取焦点
     */
    const handleFocus = (e: any, row: any, other: any) => {
        const activeElement = useActiveElement(); // 当前激活对象
        inputFocus(e, row, { ...other, el: activeElement.value?.parentNode as HTMLElement });
    };

    /**
     * 获取错误提示框偏移量
     */
    const getOffset = (x: number, y: number) => {
        return (x - y) / 2;
    };

    /**
     * 设置错误提示框偏移位置
     */
    const setOffsetPopover = (e: any) => {
        const srcElement = e.target.getBoundingClientRect();
        const popoverSize = getDom('.m-popover__main');
        const popoverRef = getDom('.m-popover');

        if (!popoverRef) return;
        const offsetWidth = getOffset(popoverSize!.scrollWidth, e.target.offsetParent.offsetWidth);

        const positionOffset = position === 'center' ? offsetWidth : 0; // 提示框展示位置 'left' | 'center'

        popoverRef!.style.cssText = `
            left: ${srcElement.left - positionOffset - 11}px;
            top: ${srcElement.top - popoverSize!.scrollHeight - 4}px;
        `;
    };

    /**
     * 设置校验 error 文案
     */
    const createValidErrorText = (element: HTMLElement, params: any, errMsg: string) => {
        const parentElement = element;

        const isExistChild = getDom('.m-error__text', parentElement); // 判断是否已经创建过，如果创建过直接返回不做任何操作

        if (isExistChild) return;

        // 创建一个新 DOM element
        const newElement = document.createElement('div');
        newElement.textContent = `${
            params.type === StrategyKey.IS_EMPTY ? errMsg : `您的输入有误，${errMsg}`
        }`;
        newElement.classList.add('fade-in');
        newElement.classList.add('m-error__text');

        parentElement.appendChild(newElement);

        // 触发动画效果
        setTimeout(function () {
            newElement.classList.add('show'); // 添加 show 类，触发淡入动画
        }, 100);
    };

    /**
     * 移除当前 Error 提示 Dom
     */
    const clearValidErrorDom = (el: HTMLElement) => {
        const hasChildErrorDom = getDom('.m-error__text', el);
        const parentElement = hasChildErrorDom ? el : el.parentNode!;

        // 获取最后一个子元素
        const lastChild = parentElement.lastChild! as HTMLElement;

        // nodeType 为 1 元素节点才执行下面操作
        if (lastChild.nodeType !== 1) return;
        if (lastChild.classList.contains('m-error__text')) {
            // 从父元素中移除最后一个子元素
            parentElement.removeChild(lastChild);
        }
    };

    /**
     * 设置当前 Error popover 提示展示状态
     */
    const setValidErrorVisible = (visible: boolean) => {
        popoverVisible.value = visible;
    };

    /**
     * 移除对应 id 校验
     * @param {string} ids 移除的 id 校验列表
     * @param {number} index 移除的 DOM element 序列号索引，应用于列表数据共同字段的场景
     */
    const resetRelatedValid = (ids: string[], index?: number) => {
        for (const id of ids) {
            const className = generateClassToId(id);
            const el =
                index != null ? getDomList(`.${className}`)?.[index] : getDom(`.${className}`)!;

            removeErrorClass(el as HTMLElement);
            if (!hideErrorText) clearValidErrorDom(el as HTMLElement);
        }
    };

    /**
     * 校验规则
     * @param {string} 错误文本挂载的元素，默认挂载在当前错误元素本身
     */
    const errValidRef = ref();
    const handleValidFieldPass = () => {
        // 验证实例
        const validInput: any = new Valid();
        for (const [index, item] of formConfig!.entries()) {
            if (item.validProps?.required) {
                validInput.add(
                    {
                        value: formFields!.value[item.modelKey!],
                        row: index,
                        id: item.validProps?.id,
                        type: StrategyKey.IS_EMPTY
                    },
                    StrategyKey.IS_EMPTY,
                    '请完善必填信息'
                );
            }

            if (isArray(item.validRules)) {
                for (const valid of item.validRules) {
                    const { validType, errMsg } = valid;
                    validInput.add(
                        {
                            value: formFields!.value[item.modelKey!],
                            row: index,
                            id: item.validProps?.id
                        },
                        validType,
                        errMsg
                    );
                }
            }
        }

        errValidRef.value = validInput.start();

        if (errValidRef.value) {
            const { params, errMsg } = errValidRef.value;
            const className = generateClassToId(params.id);
            const element = getDom(`.${className}`)!; // .q-form-item__content

            // 添加校验错误样式
            addErrorClass(element);

            // 设置校验错误文案提示
            if (!hideErrorText) {
                if (!mountErrId) {
                    createValidErrorText(element, params, errMsg);
                } else {
                    // 存在挂载 id
                    const errClassName = generateClassToId(mountErrId);
                    const mountErrElement = getDom(`.${errClassName}`)!; // 需挂载错误文本的父元素

                    params.type
                        ? createValidErrorText(mountErrElement, params, errMsg)
                        : clearValidErrorDom(mountErrElement);
                }
            }

            validErrorMessage(errMsg);
            return false;
        } else {
            if (!mountErrId) {
                // 只删除当前传入 config 对应的校验样式，解决多个校验共存问题
                const onlyValidIds = formConfig
                    ?.filter((config: any) => config.validProps?.required || config.validRules)
                    .map(item => item.validProps?.id) as Array<string>;

                resetRelatedValid(onlyValidIds);
            } else {
                // 如果存在挂载 id
                const className = generateClassToId(mountErrId);
                const el = getDom(`.${className}`)!;
                clearValidErrorDom(el);
            }
            return true;
        }
    };

    return {
        popoverVisible,
        popoverErrMsg,
        showErrorPopover,
        hideErrorPopover,
        setValidErrorVisible,
        handleInput,
        inputFocus,
        handleFocus,
        clearValidErrorDom,
        createValidErrorText,
        resetRelatedValid,
        handleValidFieldPass
    };
};
